#include "remote/client/tunnelservices.h"
#include "remote/base/basedefines.h"
#include "remote/stanza/querystanza.h"
#include "remote/base/helps.h"

namespace remote {

TunnelServices::TunnelServices(boost::asio::io_service &io_service,
                               TunnelSettings &tunnel_settings)
  : io_service_(io_service),
    tunnel_settings_(tunnel_settings) {
}

TunnelServices::~TunnelServices() {
}

bool TunnelServices::StartTunnelServer() {
  tunnel_thread_.reset(new boost::thread(
                         boost::bind(&TunnelServices::OnThreadRunning,
                                     shared_from_this())));
  return true;
}

void TunnelServices::Stop() {

}

void TunnelServices::OnThreadRunning() {
  try {
    thread_io_service_.reset(new boost::asio::io_service);
    tunnel_server_ = vzconn::VzServer::CreateTcpServer(
                       *thread_io_service_,
                       tunnel_settings_.bind_addr,
                       tunnel_settings_.bind_port);
    tunnel_server_->SignalNewConnect.connect(
      boost::bind(&TunnelServices::OnNewTunnelConnect,
                  shared_from_this(), _1, _2));
    tunnel_server_->SignalVzServerError.connect(
      boost::bind(&TunnelServices::OnTunnelServerError,
                  shared_from_this(), _1, _2));

    if(tunnel_server_->Start()) {
    } else {
    }
  } catch(std::exception &e ) {
    LOG(ERROR) << e.what();
  }
}

void TunnelServices::OnNewTunnelConnect(vzconn::VzServer::Ptr tcp_server,
                                        vzconn::VzConnect::Ptr tcp_connect) {

}

void TunnelServices::OnTunnelServerError(vzconn::VzServer::Ptr tcp_server,
    const boost::system::error_code& err) {

}

// -----------------------------------------------------------------------------

ReqTask::ReqTask(boost::asio::io_service &io_service,
                 TunnelSettings &tunnel_settings,
                 vzconn::VzConnect::Ptr client_connect)
  : io_service_(io_service),
    tunnel_settings_(tunnel_settings),
    client_connect_(client_connect) {
}

ReqTask::~ReqTask() {
}

bool ReqTask::Start() {
  server_connect_ = vzconn::VzClient::ConnectTcpServer(
                      io_service_,
                      tunnel_settings_.server_addr,
                      tunnel_settings_.server_port,
                      boost::bind(&ReqTask::OnServerConnectSucceed,
                                  shared_from_this(), _1, _2));
  return true;
}

void ReqTask::Stop() {
}

void ReqTask::OnServerConnectSucceed(vzconn::VzConnect::Ptr connect,
                                     const boost::system::error_code& err) {
  if(err) {
    LOG(ERROR) << err.message();
    SignalConnectResult(shared_from_this(),
                        TUNNEL_ERROR_CODE_FAILURE_CONNECT_SERVER);
    return ;
  }
  QueryStanza::Ptr stanza = QueryStanza::CreateQueryStanza(
                              JSON_TYPE_CREATE_TUNNEL_PROXY,
                              STATE_REQUESTION_STATE,
                              RandomString(DEFAULT_ID_SIZE),
                              tunnel_settings_.remote_name,
                              tunnel_settings_.local_name
                            );

  connect->SignalConnectError.connect(
    boost::bind(&ReqTask::OnServerConnectError,
                shared_from_this(), _1, _2));
  connect->SignalConnectWrite.connect(
    boost::bind(&ReqTask::OnServerConnectWrite,
                shared_from_this(), _1));
  connect->SignalConnectRead.connect(
    boost::bind(&ReqTask::OnServerConnectRead,
                shared_from_this(), _1, _2, _3, _4));

  connect->AsyncWrite(stanza->ToByteBuffer());
}

void ReqTask::OnServerConnectError(vzconn::VzConnect::Ptr connect,
                                   const boost::system::error_code& err) {
  SignalConnectResult(shared_from_this(), STATE_SERVER_DISCONNECTED);
}

void ReqTask::OnServerConnectWrite(vzconn::VzConnect::Ptr connect) {
}

void ReqTask::OnServerConnectRead(vzconn::VzConnect::Ptr connect,
                                  const char* buffer, int size, int flag) {
  QueryStanza::Ptr stanza(new QueryStanza);
  if(!stanza->Parse(buffer, size)) {
    LOG(ERROR) << "Failure to parse the stanza";
  }

  SignalConnectResult(shared_from_this(), stanza->state());

  if(stanza->state() != STATE_TUNNEL_CLIENT_SUCCEED) {
    connect->CloseConnect();
  }
}

}